home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 8: LINUX Games / Linux Cubed Series 8 - LINUX Games.iso / games / muds / lpmud312.tar / lpmud312 / backend.c < prev    next >
C/C++ Source or Header  |  1992-01-11  |  23KB  |  939 lines

  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <signal.h>
  4. #include <setjmp.h>
  5. #include <ctype.h>
  6. #include <sys/types.h>
  7. #include <sys/stat.h>
  8. #include <fcntl.h>
  9. #include <sys/times.h>
  10. #include <math.h>
  11. #include <memory.h>
  12.  
  13. #include "lint.h"
  14. #include "config.h"
  15. #include "interpret.h"
  16. #include "object.h"
  17. #include "wiz_list.h"
  18. #include "exec.h"
  19. #include "comm.h"
  20.  
  21. jmp_buf error_recovery_context;
  22. int error_recovery_context_exists = 0;
  23. /*
  24.  * The 'current_time' is updated at every heart beat.
  25.  */
  26. int current_time;
  27.  
  28. static void cycle_hb_list PROT((void));
  29. extern struct object *command_giver, *current_interactive, *obj_list_destruct;
  30. extern int num_player, d_flag;
  31. extern struct object *previous_ob, *master_ob;
  32.  
  33. struct object *current_heart_beat;
  34.  
  35. void call_heart_beat(), catch_alarm();
  36. void load_first_objects(), prepare_ipc(),
  37.     shutdowngame(), ed_cmd PROT((char *)),
  38.     print_prompt(), call_out(),
  39.     destruct2 PROT((struct object *));
  40.  
  41. extern int get_message PROT((char *, int)), player_parser PROT((char *)),
  42.     call_function_interactive PROT((struct interactive *, char *)),
  43.     resort_free_list(), swap PROT((struct object *));
  44.  
  45. extern void flush_all_player_mess();
  46.  
  47. extern int t_flag;
  48. int time_to_call_heart_beat;
  49. int comm_time_to_call_heart_beat = 0; /* this is set by interrupt, */
  50.     /* comm sets time_to_call_heart_beat sometime after */
  51.  
  52. /*
  53.  * There are global variables that must be zeroed before any execution.
  54.  * In case of errors, there will be a longjmp(), and the variables will
  55.  * have to be cleared explicitely. They are normally maintained by the
  56.  * code that use them.
  57.  *
  58.  * This routine must only be called from top level, not from inside
  59.  * stack machine execution (as stack will be cleared).
  60.  */
  61. void clear_state() {
  62.     extern struct object *previous_ob;
  63.  
  64.     current_object = 0;
  65.     command_giver = 0;
  66.     current_interactive = 0;
  67.     previous_ob = 0;
  68.     current_prog = 0;
  69.     error_recovery_context_exists = 1;
  70.     reset_machine(0);    /* Pop down the stack. */
  71. }
  72.  
  73. void logon(ob)
  74.     struct object *ob;
  75. {
  76.     struct svalue *ret;
  77.     struct object *save = current_object;
  78.  
  79.     /*
  80.      * current_object must be set here, so that the static "logon" in
  81.      * player.c can be called.
  82.      */
  83.     current_object = ob;
  84.     ret = apply("logon", ob, 0);
  85.     if (ret == 0) {
  86.     add_message("prog %s:\n", ob->name);
  87.     fatal("Could not find logon on the player %s\n", ob->name);
  88.     }
  89.     current_object = save;
  90. }
  91.  
  92. /*
  93.  * Take a player command and parse it.
  94.  * The command can also come from a NPC.
  95.  * Beware that 'str' can be modified and extended !
  96.  */
  97. int parse_command(str, ob)
  98.     char *str;
  99.     struct object *ob;
  100. {
  101.     struct object *save = command_giver;
  102.     int res;
  103.  
  104.     command_giver = ob;
  105.     res = player_parser(str);
  106.     command_giver = save;
  107.     return res;
  108. }
  109.  
  110. /*
  111.  * This is the backend. We will stay here for ever (almost).
  112.  */
  113. int eval_cost;
  114. void backend()
  115. {
  116.     char buff[2000];
  117.     extern int game_is_being_shut_down;
  118.     extern int slow_shut_down_to_do;
  119.  
  120.     (void)printf("Setting up ipc.\n");
  121.     fflush(stdout);
  122.     prepare_ipc();
  123.     (void)signal(SIGHUP, startshutdowngame);
  124.     if (!t_flag)
  125.     call_heart_beat();
  126.     setjmp(error_recovery_context);
  127.     while(1) {
  128.     /*
  129.      * The call of clear_state() should not really have to be done
  130.      * once every loop. However, there seem to be holes where the
  131.      * state is not consistent. If these holes are removed,
  132.      * then the call of clear_state() can be moved to just before the
  133.      * while() - statment. *sigh* /Lars
  134.      */
  135.     clear_state();
  136.     eval_cost = 0;
  137.     remove_destructed_objects(); /* marion - before ref checks! */
  138. #ifdef DEBUG
  139.     if (d_flag > 1)
  140.         check_a_lot_ref_counts(0);
  141. #endif
  142.     if (game_is_being_shut_down)
  143.         shutdowngame();
  144.     if (slow_shut_down_to_do) {
  145.         int tmp = slow_shut_down_to_do;
  146.         slow_shut_down_to_do = 0;
  147.         slow_shut_down(tmp);
  148.     }
  149.     if (get_message(buff, sizeof buff)) {
  150.         void update_load_av PROT((void));
  151.  
  152.         update_load_av();
  153.         /*
  154.          * Now we have a string from the player. This string can go to
  155.          * one of several places. If it is prepended with a '!', then
  156.          * it is an escape from the 'ed' editor, so we send it
  157.          * as a command to the parser.
  158.          * If any object function is waiting for an input string, then
  159.          * send it there.
  160.          * Otherwise, send the string to the parser.
  161.          * The player_parser() will find that current_object is 0, and
  162.          * then set current_object to point to the object that defines
  163.          * the command. This will enable such functions to be static.
  164.          */
  165.         current_object = 0;
  166.         current_interactive = command_giver;
  167.  
  168. #ifdef DEBUG
  169.         if (!command_giver->interactive)
  170.         fatal("Non interactive player in main loop !\n");
  171. #endif
  172.         if (buff[0] == '!' && command_giver->super)
  173.         parse_command(buff+1, command_giver);
  174.         else if (command_giver->interactive->ed_buffer)
  175.         ed_cmd(buff);
  176.         else if (call_function_interactive(command_giver->interactive,buff))
  177.         ;    /* Do nothing ! */
  178.         else
  179.         parse_command(buff, command_giver);
  180.         /*
  181.          * Print a prompt if player is still here.
  182.          */
  183.         if (command_giver->interactive)
  184.         print_prompt();
  185.     }
  186.     if (time_to_call_heart_beat)
  187.         call_heart_beat();
  188.     command_giver = 0;
  189.     }
  190. }
  191.  
  192. /*
  193.  * Despite the name, this routine takes care of several things.
  194.  * It will loop through all objects once every 10 minutes.
  195.  *
  196.  * If an object is found in a state of not having done reset, and the
  197.  * delay to next reset has passed, then reset() will be done.
  198.  *
  199.  * If the object has a existed more than the time limit given for swapping,
  200.  * then 'clean_up' will first be called in the object, after which it will
  201.  * be swapped out if it still exists.
  202.  *
  203.  * There are some problems if the object self-destructs in clean_up, so
  204.  * special care has to be taken of how the linked list is used.
  205.  */
  206. static void look_for_objects_to_swap() {
  207.     extern long time_to_swap; /* marion - for invocation parameter */
  208.     static int next_time;
  209.     struct object *ob;
  210.     struct object *next_ob;
  211.     jmp_buf save_error_recovery_context;
  212.     int save_rec_exists;
  213.  
  214.     if (current_time < next_time)
  215.     return;                /* Not time to look yet */
  216.     next_time = current_time + 15 * 60;    /* Next time is in 15 minutes */
  217.     memcpy((char *) save_error_recovery_context,
  218.        (char *) error_recovery_context, sizeof error_recovery_context);
  219.     save_rec_exists = error_recovery_context_exists;
  220.     /*
  221.      * Objects object can be destructed, which means that
  222.      * next object to investigate is saved in next_ob. If very unlucky,
  223.      * that object can be destructed too. In that case, the loop is simply
  224.      * restarted.
  225.      */
  226.     for (ob = obj_list; ob; ob = next_ob) {
  227.     int ready_for_swap;
  228.     if (ob->flags & O_DESTRUCTED) {
  229.         ob = obj_list; /* restart */
  230.     }
  231.     next_ob = ob->next_all;
  232.         if (setjmp(error_recovery_context)) {        /* amylaar */
  233.             extern void clear_state();
  234.             clear_state();
  235.             debug_message("Error in look_for_objects_to_swap.\n");
  236.         continue;
  237.         }
  238.     /*
  239.      * Check reference time before reset() is called.
  240.      */
  241.     if (current_time < ob->time_of_ref + time_to_swap)
  242.         ready_for_swap = 0;
  243.     else
  244.         ready_for_swap = 1;
  245.     /*
  246.      * Should this object have reset(1) called ?
  247.      */
  248.     if (ob->next_reset < current_time && !(ob->flags & O_RESET_STATE)) {
  249.         if (d_flag)
  250.         fprintf(stderr, "RESET %s\n", ob->name);
  251.         reset_object(ob, 1);
  252.     }
  253. #if TIME_TO_CLEAN_UP > 0
  254.     /*
  255.      * Has enough time passed, to give the object a chance
  256.      * to self-destruct ? Save the O_RESET_STATE, which will be cleared.
  257.      *
  258.      * Only call clean_up in objects that has defined such a function.
  259.      *
  260.      * Only if the clean_up returns a non-zero value, will it be called
  261.      * again.
  262.      */
  263.     if (current_time - ob->time_of_ref > TIME_TO_CLEAN_UP &&
  264.         (ob->flags & O_WILL_CLEAN_UP))
  265.     {
  266.         int save_reset_state = ob->flags & O_RESET_STATE;
  267.         struct svalue *svp;
  268.  
  269.         if (d_flag)
  270.         fprintf(stderr, "clean up %s\n", ob->name);
  271.         /*
  272.          * Supply a flag to the object that says if this program
  273.          * is inherited by other objects. Cloned objects might as well
  274.          * believe they are not inherited. Swapped objects will not
  275.          * have a ref count > 1 (and will have an invalid ob->prog
  276.          * pointer).
  277.          */
  278.         push_number(ob->flags & (O_CLONE|O_SWAPPED) ? 0 : ob->prog->ref);
  279.         svp = apply("clean_up", ob, 1);
  280.         if (ob->flags & O_DESTRUCTED)
  281.         continue;
  282.         if (!svp || (svp->type == T_NUMBER && svp->u.number == 0))
  283.         ob->flags &= ~O_WILL_CLEAN_UP;
  284.         ob->flags |= save_reset_state;
  285.     }
  286. #endif /* TIME_TO_CLEAN_UP > 0 */
  287. #if TIME_TO_SWAP > 0
  288.     /*
  289.      * At last, there is a possibility that the object can be swapped
  290.      * out.
  291.      */
  292.     if (ob->flags & O_SWAPPED || !ready_for_swap)
  293.         continue;
  294.     if (ob->flags & O_HEART_BEAT)
  295.         continue;
  296.     if (d_flag)
  297.         fprintf(stderr, "swap %s\n", ob->name);
  298.     swap(ob);    /* See if it is possible to swap out to disk */
  299. #endif
  300.     }
  301.     memcpy((char *) error_recovery_context,
  302.        (char *) save_error_recovery_context,
  303.        sizeof error_recovery_context);
  304.     error_recovery_context_exists = save_rec_exists;
  305. }
  306.  
  307. /*
  308.  * Call all heart_beat() functions in all objects.  Also call the next reset,
  309.  * and the call out.
  310.  * We do heart beats by moving each object done to the end of the heart beat
  311.  * list before we call its function, and always using the item at the head
  312.  * of the list as our function to call.  We keep calling heart beats until
  313.  * a timeout or we have done num_heart_objs calls.  It is done this way so
  314.  * that objects can delete heart beating objects from the list from within
  315.  * their heart beat without truncating the current round of heart beats.
  316.  *
  317.  * Set command_giver to current_object if it is a living object. If the object
  318.  * is shadowed, check the shadowed object if living. There is no need to save
  319.  * the value of the command_giver, as the caller resets it to 0 anyway.
  320.  */
  321. static struct object * hb_list = 0; /* head */
  322. static struct object * hb_tail = 0; /* for sane wrap around */
  323.  
  324. static int num_hb_objs = 0;  /* so we know when to stop! */
  325. static int num_hb_calls = 0; /* stats */
  326. static float perc_hb_probes = 100.0; /* decaying avge of how many complete */
  327.  
  328. void call_heart_beat() {
  329.     struct object *ob, *hide_current = current_object;
  330.     int num_done = 0;
  331.     
  332.     time_to_call_heart_beat = 0; /* interrupt loop if we take too long */
  333.     comm_time_to_call_heart_beat = 0;
  334. #ifndef MSDOS
  335.     (void)signal(SIGALRM, catch_alarm);
  336.     alarm(2);
  337. #else
  338.     start_timer(2);
  339. #endif
  340.     current_time = get_current_time();
  341.     current_interactive = 0;
  342.  
  343.     if ((num_player > 0) && hb_list) {
  344.         num_hb_calls++;
  345.     while (hb_list &&
  346. #ifndef MSDOS
  347.            !comm_time_to_call_heart_beat
  348. #else
  349.            !timer_expired()
  350. #endif
  351.            && (num_done < num_hb_objs)) {
  352.         num_done++;
  353.         cycle_hb_list();
  354.         ob = hb_tail; /* now at end */
  355.         if (!(ob->flags & O_HEART_BEAT))
  356.         fatal("Heart beat not set in object on heart beat list!");
  357.         if (ob->flags & O_SWAPPED)
  358.         fatal("Heart beat in swapped object.\n");
  359.         /* move ob to end of list, do ob */
  360.         if (ob->prog->heart_beat == -1)
  361.         continue;
  362.         current_prog = ob->prog;
  363.         current_object = ob;
  364.         current_heart_beat = ob;
  365.         command_giver = ob;
  366.         while(command_giver->shadowing)
  367.         command_giver = command_giver->shadowing;
  368.         if (!(command_giver->flags & O_ENABLE_COMMANDS))
  369.         command_giver = 0;
  370.         if (ob->user)
  371.         ob->user->heart_beats++;
  372.         eval_cost = 0;
  373.         call_function(ob->prog,
  374.               &ob->prog->functions[ob->prog->heart_beat]);
  375.     }
  376.     if (num_hb_objs)
  377.         perc_hb_probes = 100 * (float) num_done / num_hb_objs;
  378.     else
  379.         perc_hb_probes = 100.0;
  380.     }
  381.     current_object = hide_current;
  382.     current_heart_beat = 0;
  383.     look_for_objects_to_swap();
  384.     call_out();    /* some things depend on this, even without players! */
  385.     flush_all_player_mess();
  386.     wiz_decay();
  387. #ifdef MUDWHO
  388.     sendmudwhoinfo();
  389. #endif
  390. }
  391.  
  392. /*
  393.  * Take the first object off the heart beat list, place it at the end
  394.  */
  395. static void cycle_hb_list()
  396. {
  397.     struct object * ob;
  398.     if (!hb_list)
  399.     fatal("Cycle heart beat list with empty list!");
  400.     if (hb_list == hb_tail)
  401.     return; /* 1 object on list */
  402.     ob = hb_list;
  403.     hb_list = hb_list -> next_heart_beat;
  404.     hb_tail -> next_heart_beat = ob;
  405.     hb_tail = ob;
  406.     ob->next_heart_beat = 0;
  407. }
  408.  
  409. /*
  410.  * add or remove an object from the heart beat list; does the major check...
  411.  * If an object removes something from the list from within a heart beat,
  412.  * various pointers in call_heart_beat could be stuffed, so we must
  413.  * check current_heart_beat and adjust pointers.
  414.  */
  415.  
  416. int set_heart_beat(ob, to)
  417.     struct object * ob;
  418.     int to;
  419. {
  420.     struct object * o = hb_list;
  421.     struct object * oprev = 0;
  422.  
  423.     if (ob->flags & O_DESTRUCTED)
  424.     return 0;
  425.     if (to)
  426.     to = 1;
  427.  
  428.     while (o && o != ob) {
  429.     if (!(o->flags & O_HEART_BEAT))
  430.         fatal("Found disabled object in the active heart beat list!\n");
  431.     oprev = o;
  432.     o = o->next_heart_beat;
  433.     }
  434.  
  435.     if (!o && (ob->flags & O_HEART_BEAT))
  436.     fatal("Couldn't find enabled object in heart beat list!");
  437.     
  438.     if (to == ((ob->flags & O_HEART_BEAT) != 0))
  439.     return(0);
  440.  
  441.     if (to) {
  442.     ob->flags |= O_HEART_BEAT;
  443.     if (ob->next_heart_beat)
  444.         fatal("Dangling pointer to next_heart_beat in object!");
  445.     ob->next_heart_beat = hb_list;
  446.     hb_list = ob;
  447.     if (!hb_tail) hb_tail = ob;
  448.     num_hb_objs++;
  449.     cycle_hb_list();     /* Added by Linus. 911104 */
  450.     }
  451.     else { /* remove all refs */
  452.     ob->flags &= ~O_HEART_BEAT;
  453.     if (hb_list == ob)
  454.         hb_list = ob->next_heart_beat;
  455.     if (hb_tail == ob)
  456.         hb_tail = oprev;
  457.     if (oprev)
  458.         oprev->next_heart_beat = ob->next_heart_beat;
  459.     ob->next_heart_beat = 0;
  460.     num_hb_objs--;
  461.     }
  462.  
  463.     return(1);
  464. }
  465. /*
  466.  * sigh.  Another status function.
  467.  */
  468. int heart_beat_status(verbose)
  469.     int verbose;
  470. {
  471.     char buf[20];
  472.  
  473.     if (verbose) {
  474.     add_message("\nHeart beat information:\n");
  475.     add_message("-----------------------\n");
  476.     add_message("Number of objects with heart beat: %d, starts: %d\n",
  477.             num_hb_objs, num_hb_calls);
  478.     sprintf(buf, "%.2f", perc_hb_probes);
  479.     add_message("Percentage of HB calls completed last time: %s\n", buf);
  480.     }
  481.     return 0;
  482. }
  483.  
  484. /*
  485.  * There is a file with a list of objects to be initialized at
  486.  * start up.
  487.  */
  488.  
  489. void load_first_objects() { /* Old version used when o_flag true /JnA */
  490.     FILE *f;
  491.     char buff[1000];
  492.     char *p;
  493.     extern int e_flag;
  494. #ifndef MSDOS
  495.     struct tms tms1, tms2;
  496. #else
  497.     long timer;
  498. #endif
  499.  
  500.     if (e_flag)
  501.     return;
  502.     (void)printf("Loading init file %s\n", INIT_FILE);
  503.     f = fopen(INIT_FILE, "r");
  504.     if (f == 0)
  505.     return;
  506.     if (setjmp(error_recovery_context)) {
  507.     clear_state();
  508.     add_message("Anomaly in the fabric of world space.\n");
  509.     }
  510.     error_recovery_context_exists = 1;
  511. #ifndef MSDOS
  512.     times(&tms1);
  513. #else
  514.     timer = 0L;
  515.     (void) milliseconds(&timer);
  516. #endif
  517.     while(1) {
  518.     if (fgets(buff, sizeof buff, f) == NULL)
  519.         break;
  520.     if (buff[0] == '#')
  521.         continue;
  522.     p = strchr(buff, '\n');
  523.     if (p != 0)
  524.         *p = 0;
  525.     if (buff[0] == '\0')
  526.         continue;
  527.     (void)printf("Preloading: %s", buff);
  528.     fflush(stdout);
  529.     eval_cost = 0;
  530.     (void)find_object(buff);
  531. #ifdef MALLOC_malloc
  532.     resort_free_list();
  533. #endif
  534. #ifndef MSDOS
  535.     times(&tms2);
  536.     (void)printf(" %.2f\n", (tms2.tms_utime - tms1.tms_utime +
  537.                  tms2.tms_stime - tms1.tms_stime) / 60.0);
  538.     tms1 = tms2;
  539. #else
  540.     (void)printf(" %.2f\n", milliseconds(&timer)/1000.0);
  541. #endif
  542.     fflush(stdout);
  543.     }
  544.     error_recovery_context_exists = 0;
  545.     fclose(f);
  546. }
  547.  
  548. /*
  549.  * New version used when not in -o mode. The epilog() in master.c is
  550.  * supposed to return an array of files (castles in 2.4.5) to load. The array
  551.  * returned by apply() will be freed at next call of apply(), which means that
  552.  * the ref count has to be incremented to protect against deallocation.
  553.  *
  554.  * The master object is asked to do the actual loading.
  555.  */
  556. void preload_objects(eflag)
  557.     int eflag;
  558. {
  559.     struct vector *prefiles;
  560.     struct svalue *ret;
  561.     int ix;
  562.  
  563.     push_number(eflag);
  564.     ret = apply_master_ob("epilog", 1);
  565.  
  566.     if ((ret == 0) || (ret->type != T_POINTER))
  567.     return;
  568.     else
  569.     prefiles = ret->u.vec;
  570.  
  571.     if ((prefiles == 0) || (prefiles->size < 1))
  572.     return;
  573.  
  574.     prefiles->ref++;
  575.  
  576.     ix = -1;
  577.     if (setjmp(error_recovery_context)) {
  578.     clear_state();
  579.     add_message("Anomaly in the fabric of world space.\n");
  580.     }
  581.     error_recovery_context_exists = 1;
  582.  
  583.     while (++ix < prefiles->size) {
  584.     if (prefiles->item[ix].type != T_STRING)
  585.         continue;
  586.  
  587.     eval_cost = 0;
  588.     push_string(prefiles->item[ix].u.string, STRING_MALLOC);
  589.     (void)apply_master_ob("preload", 1);
  590.  
  591. #ifdef MALLOC_malloc
  592.     resort_free_list();
  593. #endif
  594.     }
  595.     free_vector(prefiles);
  596.     error_recovery_context_exists = 0;
  597. }
  598.  
  599. /*
  600.  * catch alarm, set flag for comms code and heart_beat to catch.
  601.  * comms code sets time_to_call_heart_beat for the backend when
  602.  * it has completed the current round of player commands.
  603.  */
  604.  
  605. void catch_alarm() {
  606.     comm_time_to_call_heart_beat = 1;
  607. }
  608.  
  609. /*
  610.  * All destructed objects are moved int a sperate linked list,
  611.  * and deallocated after program execution.
  612.  */
  613. void remove_destructed_objects()
  614. {
  615.     struct object *ob, *next;
  616.     for (ob=obj_list_destruct; ob; ob = next) {
  617.     next = ob->next_all;
  618.     destruct2(ob);
  619.     }
  620.     obj_list_destruct = 0;
  621. }
  622.  
  623. /*
  624.  * Append string to file. Return 0 for failure, otherwise 1.
  625.  */
  626. int write_file(file, str)
  627.     char *file;
  628.     char *str;
  629. {
  630.     FILE *f;
  631.  
  632. #ifdef COMPAT_MODE
  633.     file = check_file_name(file, 1);
  634. #else
  635.     file = check_valid_path(file, current_object->eff_user, "write_file", 1);
  636. #endif
  637.     if (!file)
  638.     return 0;
  639.     f = fopen(file, "a");
  640.     if (f == 0)
  641.     error("Wrong permissions for opening file %s for append.\n", file);
  642.     fwrite(str, strlen(str), 1, f);
  643.     fclose(f);
  644.     return 1;
  645. }
  646.  
  647. char *read_file(file,start,len)
  648.     char *file;
  649.     int start,len;
  650. {
  651.     struct stat st;
  652.     FILE *f;
  653.     char *str,*p,*p2,*end,c;
  654.     int size;
  655.  
  656.     if (len < 0) return 0;
  657. #ifdef COMPAT_MODE
  658.     file = check_file_name(file, 0);
  659. #else    
  660.     file = check_valid_path(file, current_object->eff_user, "read_file", 0);
  661. #endif    
  662.  
  663.     if (!file)
  664.     return 0;
  665.     f = fopen(file, "r");
  666.     if (f == 0)
  667.     return 0;
  668.     if (fstat(fileno(f), &st) == -1)
  669.     fatal("Could not stat an open file.\n");
  670.     size = st.st_size;
  671.     if (size > READ_FILE_MAX_SIZE) {
  672.     if ( start || len ) size = READ_FILE_MAX_SIZE;
  673.     else {
  674.         fclose(f);
  675.         return 0;
  676.     }
  677.     }
  678.     if (!start) start = 1;
  679.     if (!len) len = READ_FILE_MAX_SIZE;
  680.     str = xalloc(size + 1);
  681.     str[size] = '\0';
  682.     do {
  683.     if (size > st.st_size)
  684.         size = st.st_size;
  685.         if (fread(str, size, 1, f) != 1) {
  686.             fclose(f);
  687.         free(str);
  688.             return 0;
  689.         }
  690.     st.st_size -= size;
  691.     end = str+size;
  692.         for (p=str; ( p2=memchr(p,'\n',end-p) ) && --start; ) p=p2+1;
  693.     } while ( start > 1 );
  694.     for (p2=str; p != end; ) {
  695.         c = *p++;
  696.     if ( !isprint(c) && !isspace(c) ) c=' ';
  697.     *p2++=c;
  698.     if ( c == '\n' )
  699.         if (!--len) break;
  700.     }
  701.     if ( len && st.st_size ) {
  702.     size -= ( p2-str) ; 
  703.     if (size > st.st_size)
  704.         size = st.st_size;
  705.         if (fread(p2, size, 1, f) != 1) {
  706.             fclose(f);
  707.         free(str);
  708.             return 0;
  709.         }
  710.     st.st_size -= size;
  711.     end = p2+size;
  712.         for (; p2 != end; ) {
  713.         c = *p2;
  714.         if ( !isprint(c) && !isspace(c) ) *p2=' ';
  715.         p2++;
  716.         if ( c == '\n' )
  717.             if (!--len) break;
  718.     }
  719.     if ( st.st_size && len ) {
  720.         /* tried to read more than READ_MAX_FILE_SIZE */
  721.         fclose(f);
  722.         free(str);
  723.         return 0;
  724.     }
  725.     }
  726.     *p2='\0';
  727.     fclose(f);
  728. #if 0 /* caller immediately frees the string again,
  729.        * so there's no use to make it smaller now
  730.        */
  731.     if ( st.st_size > (p2-str) ) {
  732. /* can't allocate shared string when string type isn't passed to the caller */
  733.     p2=strdup(str);
  734.     free(str);
  735.     return p2;
  736.     }
  737. #endif
  738.     return str;
  739. }
  740.  
  741.  
  742. char *read_bytes(file,start,len)
  743.     char *file;
  744.     int start,len;
  745. {
  746.     struct stat st;
  747.  
  748.     char *str,*p;
  749.     int size, f;
  750.     int lseek();
  751.  
  752.     if (len < 0)
  753.     return 0;
  754.     if(len > MAX_BYTE_TRANSFER)
  755.     return 0;
  756. #ifdef COMPAT_MODE
  757.     file = check_file_name(file, 0);
  758. #else    
  759.     file = check_valid_path(file, current_object->eff_user, 
  760.                 "read_bytes", 0);
  761. #endif    
  762.  
  763.     if (!file)
  764.     return 0;
  765.     f = open(file, O_RDONLY);
  766.     if (f < 0)
  767.     return 0;
  768.  
  769.     if (fstat(f, &st) == -1)
  770.     fatal("Could not stat an open file.\n");
  771.     size = st.st_size;
  772.     if(start < 0) 
  773.     start = size + start;
  774.  
  775.     if (start >= size) {
  776.     close(f);
  777.     return 0;
  778.     }
  779.     if ((start+len) > size) 
  780.     len = (size - start);
  781.  
  782.     if ((size = lseek(f,start, 0)) < 0)
  783.     return 0;
  784.  
  785.     str = xalloc(len + 1);
  786.  
  787.     size = read(f, str, len);
  788.  
  789.     close(f);
  790.  
  791.     if (size <= 0) {
  792.     free(str);
  793.     return 0;
  794.     }
  795.  
  796.     /* We want to allow all characters to pass untouched!
  797.     for (il=0;il<size;il++) 
  798.     if (!isprint(str[il]) && !isspace(str[il]))
  799.         str[il] = ' ';
  800.  
  801.     str[il] = 0;
  802.     */
  803.     /*
  804.      * The string has to end to '\0'!!!
  805.      */
  806.     str[size] = '\0';
  807.  
  808.     p = string_copy(str);
  809.     free(str);
  810.  
  811.     return p;
  812. }
  813.  
  814. int write_bytes(file,start,str)
  815.     char *file, *str;
  816.     int start;
  817. {
  818.     struct stat st;
  819.  
  820.     int size, f;
  821.     int lseek();
  822.  
  823. #ifdef COMPAT_MODE    
  824.     file = check_file_name(file, 1);
  825. #else    
  826.     file = check_valid_path(file, current_object->eff_user, 
  827.                 "write_bytes", 1);
  828. #endif    
  829.  
  830.     if (!file)
  831.     return 0;
  832.     if(strlen(str) > MAX_BYTE_TRANSFER)
  833.     return 0;
  834.     f = open(file, O_WRONLY);
  835.     if (f < 0)
  836.     return 0;
  837.  
  838.     if (fstat(f, &st) == -1)
  839.     fatal("Could not stat an open file.\n");
  840.     size = st.st_size;
  841.     if(start < 0) 
  842.     start = size + start;
  843.  
  844.     if (start >= size) {
  845.     close(f);
  846.     return 0;
  847.     }
  848.     if ((start+strlen(str)) > size) 
  849.     return 0;
  850.  
  851.     if ((size = lseek(f,start, 0)) < 0)
  852.     return 0;
  853.  
  854.     size = write(f, str, strlen(str));
  855.  
  856.     close(f);
  857.  
  858.     if (size <= 0) {
  859.     return 0;
  860.     }
  861.  
  862.     return 1;
  863. }
  864.  
  865.  
  866. int file_size(file)
  867.     char *file;
  868. {
  869.     struct stat st;
  870.  
  871. #ifdef COMPAT_MODE
  872.     file = check_file_name(file, 0);
  873. #else
  874.     file = check_valid_path(file, current_object->eff_user, "file_size", 0);
  875. #endif
  876.     if (!file)
  877.     return -1;
  878.     if (stat(file, &st) == -1)
  879.     return -1;
  880.     if (S_IFDIR & st.st_mode)
  881.     return -2;
  882.     return st.st_size;
  883. }
  884.  
  885. static double load_av = 0.0;
  886.  
  887. void update_load_av() {
  888.     extern double consts[5];
  889.     extern int current_time;
  890.     static int last_time;
  891.     int n;
  892.     double c;
  893.     static int acc = 0;
  894.  
  895.     acc++;
  896.     if (current_time == last_time)
  897.     return;
  898.     n = current_time - last_time;
  899.     if (n < sizeof consts / sizeof consts[0])
  900.     c = consts[n];
  901.     else
  902.     c = exp(- n / 900.0);
  903.     load_av = c * load_av + acc * (1 - c) / n;
  904.     last_time = current_time;
  905.     acc = 0;
  906. }
  907.  
  908. static double compile_av = 0.0;
  909.  
  910. void update_compile_av(lines)
  911.     int lines;
  912. {
  913.     extern double consts[5];
  914.     extern int current_time;
  915.     static int last_time;
  916.     int n;
  917.     double c;
  918.     static int acc = 0;
  919.  
  920.     acc += lines;
  921.     if (current_time == last_time)
  922.     return;
  923.     n = current_time - last_time;
  924.     if (n < sizeof consts / sizeof consts[0])
  925.     c = consts[n];
  926.     else
  927.     c = exp(- n / 900.0);
  928.     compile_av = c * compile_av + acc * (1 - c) / n;
  929.     last_time = current_time;
  930.     acc = 0;
  931. }
  932.  
  933. char *query_load_av() {
  934.     static char buff[100];
  935.  
  936.     sprintf(buff, "%.2f cmds/s, %.2f comp lines/s", load_av, compile_av);
  937.     return buff;
  938. }
  939.